#include "i2c.h"
#include "delay.h"

static void GpioInit(void)
{
    rcu_periph_clock_enable(I2C_CLK);

    gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C_SCL_PIN);
    gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN);

    gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C_SDA_PIN);
    gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
}

static void I2CDelay(uint32_t us)
{
    // volatile uint32_t i = 0;

    // for (; us > 0; us--)
    // {
    //     i = 30;
    //     while (i--)
    //         ;
    // }
    DelayUs(us);
}

void I2CStart(void)
{
    I2C_SDA_SET();
    I2C_SCL_SET();
    I2CDelay(4);
    I2C_SDA_RESET();
    I2CDelay(4);
    I2C_SCL_RESET();
    I2CDelay(4);
}

void I2CStop(void)
{
    I2C_SDA_RESET();
    I2CDelay(4);
    I2C_SCL_SET();
    I2CDelay(4);
    I2C_SDA_SET();
    I2CDelay(4);
}

void I2CSendByte(uint8_t byte)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        if (byte & 0x80)
        {
            I2C_SDA_SET();
        }
        else
        {
            I2C_SDA_RESET();
        }

        byte <<= 1;

        I2CDelay(4);
        I2C_SCL_SET();
        I2CDelay(4);
        I2C_SCL_RESET();
        I2CDelay(4);
    }
}

uint8_t I2CReadByte(void)
{
    uint8_t byte = 0;

    I2C_SDA_SET();
    for (uint8_t i = 0; i < 8; i++)
    {
        I2C_SCL_SET();
        I2CDelay(4);
        byte <<= 1;

        if (I2C_SDA_READ())
        {
            byte++;
        }
        I2C_SCL_RESET();
        I2CDelay(4);
    }
    return byte;
}

uint8_t I2CWaitAck(void)
{
    uint16_t errTimes = 0;

    I2C_SDA_SET();
    I2CDelay(4);
    I2C_SCL_SET();
    I2CDelay(4);

    while (I2C_SDA_READ())
    {
        errTimes++;
        if (errTimes > 250)
        {
            I2CStop();
            return 1;
        }
    }

    I2C_SCL_RESET();
    I2CDelay(4);

    return 0;
}

void I2CSendAck(void)
{
    I2C_SDA_RESET();
    I2CDelay(4);
    I2C_SCL_SET();
    I2CDelay(4);
    I2C_SCL_RESET();
    I2CDelay(4);
    I2C_SDA_SET();
}

void I2CSendNack(void)
{
    I2C_SDA_SET();
    I2CDelay(4);
    I2C_SCL_SET();
    I2CDelay(4);
    I2C_SCL_RESET();
    I2CDelay(4);
}

void I2CInit(void)
{
    GpioInit();
    I2CStop();
}

uint8_t I2CWrite(uint8_t deviceAddr, uint8_t *data, uint8_t dataLen)
{
    I2CStart();
    I2CSendByte(deviceAddr);

    if (I2CWaitAck())
    {
        I2CStop();
        return 1;
    }

    for (uint8_t i = 0; i < dataLen; i++)
    {
        I2CSendByte(data[i]);
        if (I2CWaitAck())
        {
            I2CStop();
            return 1;
        }
    }
    I2CStop();

    return 0;
}

uint8_t I2CRead(uint8_t deviceAddr, uint8_t *data, uint8_t dataLen)
{
    I2CStart();
    I2CSendByte(deviceAddr);

    if (I2CWaitAck())
    {
        I2CStop();
        return 1;
    }

    while (dataLen)
    {
        if (dataLen == 1)
        {
            *data = I2CReadByte();
            I2CSendNack();
        }
        else
        {
            *data = I2CReadByte();
            I2CSendAck();
        }
        dataLen--;
        data++;
    }
    I2CStop();

    return 0;
}
